home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 2: CDPD 1
/
Almathera Ten on Ten - Disc 2: CDPD 1.iso
/
pd
/
076-100
/
079
/
kill
/
kill.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-13
|
15KB
|
508 lines
/*
* KILL version 1.01
* by George Musser Jr.
*
* 11 Jan 87 - initial version
* 24 Jan 87 - send ACTION_DIE packet to ConsoleTask for Wbench processes
*
* Removes a task and as much of its hinterland as possible. We can
* close windows and unload process code, but screens and other,
* unidentified structures elude us. We can remove all but 40 or so
* bytes of an empty CLI.
*
* Syntax: KILL <task name>
* KILL <task pointer>
*
* I certainly don't claim that this program is the proper way to deal with
* the Amiga system. Hopefully, DOS 1.3 will come with a legitimate way to
* kill runaway and undesirable processes.
*
* Things to try in the next version:
* - improve the killing of Workbench-spawned processes (somehow get
* at the stdin window via process->pr_ConsoleTask);
* - try to kill a non-process;
* - experiment with trackdisk.device (killing the device stops
* the drive from clicking; perhaps a SleepAmiga utility could
* kill trackdisk, await a keypress, and Mount the drive);
* - how does the system associate the CON devices with windows?
* maybe we can use this to kill devices and Workbench processes
* more throrughly.
*
* Geo's net addresses:
* CIS 76566,3714 (log on at least biweekly)
* Plink OHS152 (log on at least monthly)
* ST801115@BROWNVM.BITNET (log on at least weekly)
*/
/***** Includes ***********************************************************/
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/memory.h>
#include <exec/tasks.h>
#include <exec/ports.h>
#include <exec/execbase.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <workbench/startup.h>
#include <stdio.h>
/***** Defines ************************************************************/
#define LIMIT1_OF_PATIENCE 1 /* Time to wait for death by CLOSEWINDOW */
#define LIMIT2_OF_PATIENCE 1 /* Time to wait for death by CTRL-C */
#define LIMIT3_OF_PATIENCE 1 /* Time to wait for console to die */
#define MAX_WINDOWS 25 /* Maximum number of windows we can close */
/***** Global variables ***************************************************/
struct IntuitionBase *IntuitionBase;
extern struct DosLibrary *DOSBase;
/***** main() *************************************************************/
main (argc,argv)
char **argv;
int argc;
{
void exit();
struct IntuitionBase *OpenLibrary(char *,long);
int Kill(char *);
void CloseLibrary();
int rc;
if (argc < 2) /* Not enough arguments, so quit */
exit (RETURN_ERROR);
IntuitionBase = OpenLibrary("intuition.library",LIBRARY_VERSION);
rc = Kill(*++argv);
if (IntuitionBase)
CloseLibrary (IntuitionBase);
exit (rc);
}
/***** Kill() *************************************************************
*
* Kill() is a function so that we can experiment with recursion, which
* may be needed to kill the console task.
*
*/
int Kill (name)
char *name;
{
/***** Functions *******************************************************/
int stch_i();
void Forbid();
struct Task *FindTask(char *);
void Permit();
void fprintf();
ULONG LockIBase(long);
void UnlockIBase(long);
APTR AllocMem(long,long);
void PutMsg();
void Delay(long);
void CloseLibrary();
void FreeMem();
void Signal(struct Task *,long);
void UnLock(BPTR);
void UnLoadSeg(BPTR);
void Close(BPTR);
struct Message *GetMsg(struct MsgPort *);
struct MsgPort *FindPort(char *);
void ReplyMsg();
void RemTask(struct Task *);
void ClearMenuStrip(struct Window *);
void CloseWindow(struct Window *);
/***** Local variables *************************************************/
ULONG ilock; /* Lock on IntuitionBase, so rug stays under us */
struct Task *task; /* This will point to the task we must remove */
struct RootNode *rootnode; /* Central AmigaDOS structure */
APTR *TaskTable; /* List of processes */
struct Screen *screen; /* Points to a screen */
struct Window *window; /* ...and to a window */
int nWindow; /* Number of task's windows */
struct Window *WindowList[MAX_WINDOWS]; /* Windows opened by task */
unsigned long i; /* Loop counter */
struct IntuiMessage *message; /* Our forged IntuiMessage */
struct Process *process; /* Oops, task is part of a process */
struct CommandLineInterface *cli; /* What's more, it's a CLI process */
BPTR *SegArray; /* A process has a list of segments */
unsigned long nSeg; /* Number of process segments */
struct MsgPort *myport; /* Reply port for packet */
struct StandardPacket *packet; /* Message to shut down console */
struct WBStartup *startup; /* Forged message to Workbench */
struct DOSLibrary *dos; /* So that we can close DOS library */
/***** Here we go ******************************************************/
Forbid();
if (stch_i(name,&task) < 3) /* If user gave a string, */
if ((task = FindTask(name)) == NULL) { /* find the corresponding */
Permit(); /* task. */
fprintf (stderr,"Can't find `%s'.\n",name);
return (RETURN_ERROR);
}
if (task == FindTask(NULL)) { /* No suicide pills here */
Permit();
fprintf (stderr,"Can't kill self.\n");
return (RETURN_ERROR);
}
#ifdef DEBUG
Permit();
printf ("Killing task %lx\n",task);
Forbid();
#endif DEBUG
/* Find windows associated with the task */
nWindow = 0;
if (IntuitionBase) {
ilock = LockIBase(0);
/* Loop through screens */
screen = IntuitionBase->FirstScreen;
while (screen && nWindow < MAX_WINDOWS) {
/* Loop through windows */
window = screen->FirstWindow;
while (window && nWindow < MAX_WINDOWS) {
/* If this window has an IDCMP, then it points back to
* the task. Check if this task is the one we want.
*/
if (window->UserPort->mp_SigTask == task)
WindowList[nWindow++] = window;
window = window->NextWindow;
} /* windows */
screen = screen->NextScreen;
} /* screens */
UnlockIBase (ilock);
} /* IntuitionBase */
/* First we'll give the task a chance to die quietly. Fake an Intuition
* CLOSEWINDOW message and send it to an IDCMP equipped to handle such
* an event. Give the task a second to die and check if it's still
* around. If so...
*/
if (nWindow) {
message = (struct IntuiMessage *)AllocMem(sizeof(struct IntuiMessage),
MEMF_PUBLIC);
i = nWindow;
while (i)
if (WindowList[--i]->IDCMPFlags & CLOSEWINDOW) {
message->Class = CLOSEWINDOW;
message->IDCMPWindow = WindowList[i];
message->ExecMessage.mn_ReplyPort = WindowList[i]->WindowPort;
message->ExecMessage.mn_Node.ln_Type = NT_MESSAGE;
message->ExecMessage.mn_Length = sizeof(struct IntuiMessage) -
sizeof(struct Message);
Permit();
PutMsg (WindowList[i]->UserPort,message);
Delay (LIMIT1_OF_PATIENCE * TICKS_PER_SECOND);
Forbid();
FreeMem (message,sizeof(struct IntuiMessage));
if (FindTask(name) != task) {
Permit();
return (RETURN_OK);
}
}
}
if (task->tc_Node.ln_Type == NT_PROCESS) {
/* Plan B. Try breaking the task with the CTRL-C/CTRL-D combination.
* Wait a little while and check whether the task is still there.
* Even if the plan doesn't kill the task, it'll break out of the
* current CLI command and thereby allow us to close the CLI window.
*/
Permit();
#ifdef DEBUG
printf ("Sending break signals...");
#endif DEBUG
Signal (task,SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D);
Delay (LIMIT2_OF_PATIENCE * TICKS_PER_SECOND);
Forbid();
if (FindTask(name) != task) {
Permit();
#ifdef DEBUG
puts ("");
#endif DEBUG
return (RETURN_OK);
}
#ifdef DEBUG
Permit();
printf ("done.\n");
Forbid();
#endif DEBUG
/* Well, looks as though we'll have to pry it loose by hand. */
process = (struct Process *)task;
SegArray = (BPTR *)BADDR(process->pr_SegList);
#ifdef DEBUG
Permit();
printf ("Killing process %lx\n",&process->pr_MsgPort);
Forbid();
#endif DEBUG
/* Delete the process from AmigaDOS's list. First, dig up the master
* list of processes. Second, scan through the list for our task and
* reset its pointer to NULL.
*/
rootnode = (struct RootNode *)DOSBase->dl_Root;
TaskTable = (APTR *)BADDR(rootnode->rn_TaskArray);
for (i = 1; i <= (unsigned long)TaskTable[0]; i++)
if (TaskTable[i] == (APTR)&process->pr_MsgPort) {
TaskTable[i] = NULL;
break;
}
if (process->pr_TaskNum) {
/* It's not a child of Workbench, so we will have to clean up.
* First, unlock the current directory. Second, check whether
* the CLI is in the midst of executing a command. If so,
* unload the command's code. If not, close the CLI's I/O.
* Finally, unload the CLI's code.
*/
UnLock (process->pr_CurrentDir);
cli = (struct CommandLineInterface *)BADDR(process->pr_CLI);
if (cli->cli_Module)
UnLoadSeg (cli->cli_Module);
else {
/* Close CLI I/O files, if the CLI isn't executing a command.
* I tried to close cli_StandardInput, et. al., and to check
* whether any other processes used these file handles as their
* CLI I/O. These additional measures had no effect. Under
* the present code, if you kill a CLI from which a background
* CLI had spawned, AmigaDOS will wait until the background
* process is done before closing the CLI window.
*
* Closing files when the CLI was in the midst of executing a
* command crashed the system. The CTRL-C/CTRL-D combination
* above helps.
*/
if (cli->cli_Interactive) {
if (process->pr_CIS) Close (process->pr_CIS);
if (process->pr_COS) Close (process->pr_COS);
}
} /* else cli->cli_Module */
/* Remove process code and table thereof */
nSeg = SegArray[0];
while (--nSeg > 2)
UnLoadSeg (SegArray[nSeg]);
FreeMem (SegArray,SegArray[0] * sizeof(BPTR));
} /* if (process->pr_TaskNum) */
else {
/* Send a death message to the ConsoleTask associated with this
* process. I'm not sure if this actually does anything, but
* but it's part of an attempt to close the standard I/O window
* opened by the startup code.
*/
if (process->pr_ConsoleTask) {
myport = &((struct Process *)FindPort(NULL))->pr_MsgPort;
packet = (struct StandardPacket *)
AllocMem(sizeof(struct StandardPacket),MEMF_PUBLIC);
packet->sp_Msg.mn_Node.ln_Type = NT_MESSAGE;
packet->sp_Msg.mn_Node.ln_Name = (char *)&(packet->sp_Pkt);
packet->sp_Msg.mn_ReplyPort = myport;
packet->sp_Msg.mn_Length = sizeof(struct DosPacket);
packet->sp_Pkt.dp_Link = &(packet->sp_Msg);
packet->sp_Pkt.dp_Port = packet->sp_Msg.mn_ReplyPort;
packet->sp_Pkt.dp_Type = ACTION_DIE;
Permit();
#ifdef DEBUG
printf ("Sending message %lx to %lx...",
packet,process->pr_ConsoleTask);
#endif DEBUG
PutMsg (process->pr_ConsoleTask,packet);
Delay (LIMIT3_OF_PATIENCE * TICKS_PER_SECOND);
if (GetMsg(myport) == &packet->sp_Msg)
FreeMem (packet,sizeof(struct StandardPacket));
#ifdef DEBUG
printf ("done.\n");
#endif DEBUG
Forbid();
}
/* We're killing a child of Workbench. Let Workbench mop up the
* mess. Fake a message to Workbench in reply to its startup
* message.
*/
startup = (struct WBStartup *)AllocMem(sizeof(struct WBStartup),
MEMF_PUBLIC);
startup->sm_Message.mn_Node.ln_Type = NT_MESSAGE;
startup->sm_Message.mn_ReplyPort = FindPort("Workbench");
startup->sm_Message.mn_Length = sizeof(struct WBStartup) -
sizeof(struct Message);
startup->sm_Process = &process->pr_MsgPort;
startup->sm_Segment = SegArray[3];
startup->sm_NumArgs = 0;
startup->sm_ToolWindow = NULL;
startup->sm_ArgList = NULL;
Permit();
#ifdef DEBUG
printf ("Sending message %lx...",startup);
#endif DEBUG
ReplyMsg (startup);
#ifdef DEBUG
printf ("done.\n");
#endif DEBUG
Forbid();
} /* else (process->pr_TaskNum) */
/* Close libraries opened by process */
#ifdef DEBUG
Permit();
printf ("Closing DOS...");
Forbid();
#endif DEBUG
dos = (struct DOSLibrary *)DOSBase;
CloseLibrary (dos);
#ifdef DEBUG
Permit();
printf ("done.\n");
Forbid();
#endif DEBUG
/* At last we can kill the task. Besides pulling the task from
* Exec's sight, RemTask() seems to free the Task structure, the task
* stack, and memory that is specified in the task's tc_MemEntry list
* (verified empirically). For processes, tc_MemEntry covers the
* Process structure and the process stack.
*/
#ifdef DEBUG
Permit();
printf ("Killing task...");
Forbid();
#endif DEBUG
RemTask (task);
#ifdef DEBUG
Permit();
printf ("done.\n");
Forbid();
#endif DEBUG
} /* process */
else {
/* I haven't experimented with non-processes yet. For now, all
* we do is remove the task from Exec's list.
*/
RemTask (task);
FreeMem (task,sizeof(struct Task));
}
/* Close windows */
#ifdef DEBUG
Permit();
printf ("Closing windows...");
Forbid();
#endif
while (nWindow) {
if (WindowList[--nWindow]->MenuStrip)
ClearMenuStrip (WindowList[nWindow]);
CloseWindow (WindowList[nWindow]);
}
#ifdef DEBUG
Permit();
printf ("done.\n");
Forbid();
#endif DEBUG
Permit();
return (RETURN_OK);
}